#include "BitStream.h"

BitStream::BitStream()
{
	this->offsetRead = 0;
	this->offsetWrite = 0;
};

bool BitStream::eos()
{
	return offsetRead == offsetWrite;
}

bool BitStream::get()
{
	if(!eos())
		return bits[offsetRead++];

	return NULL;
}


unsigned short BitStream::getBitCode(unsigned int width)
{
	if(width < 0)
		throw new BitStreamInsufficientBits();

	if(offsetRead + width > offsetWrite)
		throw new BitStreamInsufficientBits();

	unsigned short ret = 0;
	for(unsigned int i = 0; i < width; ++i)
		ret |= bits[offsetRead++] << (width - i - 1);

	return ret;
}

unsigned int BitStream::size()
{
	return offsetWrite;
}

void BitStream::write(bool bit)
{
	bits.push_back(bit);
	++offsetWrite;
}

void BitStream::write(unsigned char byte)
{
	bits.push_back(byte >> 7);
	bits.push_back((byte >> 6) & 0x01);
	bits.push_back((byte >> 5) & 0x01);
	bits.push_back((byte >> 4) & 0x01);
	bits.push_back((byte >> 3) & 0x01);
	bits.push_back((byte >> 2) & 0x01);
	bits.push_back((byte >> 1) & 0x01);
	bits.push_back(byte & 0x01);
	offsetWrite += 8;
}

void BitStream::write(const bool bit, bool first)
{
	if(!first)
		write(bit);

	offsetWrite++;
	bits.insert(bits.begin(), bit);
}

void BitStream::write(unsigned short bitCode)
{
	// Assuming 12-bit fixed length
	bits.push_back(bitCode >> 11);
	bits.push_back((bitCode >> 10) & 0x01);
	bits.push_back((bitCode >> 9) & 0x01);
	bits.push_back((bitCode >> 8) & 0x01);
	bits.push_back((bitCode >> 7) & 0x01);
	bits.push_back((bitCode >> 6) & 0x01);
	bits.push_back((bitCode >> 5) & 0x01);
	bits.push_back((bitCode >> 4) & 0x01);
	bits.push_back((bitCode >> 3) & 0x01);
	bits.push_back((bitCode >> 2) & 0x01);
	bits.push_back((bitCode >> 1) & 0x01);
	bits.push_back(bitCode & 0x01);
	offsetWrite += 8;
}

void BitStream::write(BitStream * bs)
{
	for(unsigned int i = 0; i < bs->size(); ++i)
	{
		this->write(bs->bits[i]);
	}
	if(bits.size() > 256 * 256)
		throw new BitStreamFull();
}

void BitStream::store(ofstream *file)
{
	if(!file->is_open())
		return;

	char * buffer = Bit::toByteArray(bits);

	if(bits.size() <= 256 * 256)
	{
		file->write(buffer, bits.size() / 8 + 1);
		delete buffer;
		this->flush();
		return;
	}
	file->write(buffer, bits.size() / 8);

	unsigned int leftOvers = bits.size() % 8;
	vector<bool> bitsTemp;
	for(unsigned int i = 0; i < leftOvers; ++i)
	{
		bitsTemp.push_back(bits[bits.size() - leftOvers + i]);
	}
	this->flush();
	delete buffer;
	for(unsigned int i = 0; i < bitsTemp.size(); ++i)
	{
		bits.push_back(bitsTemp[i]);
		offsetWrite++;
	}

}

void BitStream::flush()
{
	bits.clear();
	offsetWrite = 0;
}
